[codex] Add JS virtual filesystem mounts#482
Conversation
Merging this PR will not alter performance
Comparing Footnotes
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
3 issues found across 4 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="crates/monty-js/wrapper.ts">
<violation number="1" location="crates/monty-js/wrapper.ts:145">
P2: `writeBytesLimit` validation accepts NaN and Infinity, which silently disable write quota enforcement</violation>
<violation number="2" location="crates/monty-js/wrapper.ts:1128">
P2: Write limit quota is charged before backend write success, with no rollback on failure</violation>
</file>
<file name="crates/monty-js/src/monty_cls.rs">
<violation number="1" location="crates/monty-js/src/monty_cls.rs:685">
P1: Fallible callback setup after REPL/mount state is consumed can permanently orphan shared REPL state. After `take_shared_repl` removes the REPL from the shared `Arc<Mutex>`, fallible `CallbackStringPrint::new_js_ref` calls inside `feed_start`, `feed_with_mounts`, and `repl_progress_to_result_with_mounts` can error out via `?` before the REPL (or progress containing the REPL) is restored. On those early-return paths `put_shared_repl` is never reached, so the mutex stays `None` and the REPL is lost forever.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| macro_rules! feed_start_impl { | ||
| ($repl:expr) => {{ | ||
| let mut print_cb = match &print_callback_ref { | ||
| Some(func) => Some(CallbackStringPrint::new_js_ref(env, func)?), |
There was a problem hiding this comment.
P1: Fallible callback setup after REPL/mount state is consumed can permanently orphan shared REPL state. After take_shared_repl removes the REPL from the shared Arc<Mutex>, fallible CallbackStringPrint::new_js_ref calls inside feed_start, feed_with_mounts, and repl_progress_to_result_with_mounts can error out via ? before the REPL (or progress containing the REPL) is restored. On those early-return paths put_shared_repl is never reached, so the mutex stays None and the REPL is lost forever.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/monty-js/src/monty_cls.rs, line 685:
<comment>Fallible callback setup after REPL/mount state is consumed can permanently orphan shared REPL state. After `take_shared_repl` removes the REPL from the shared `Arc<Mutex>`, fallible `CallbackStringPrint::new_js_ref` calls inside `feed_start`, `feed_with_mounts`, and `repl_progress_to_result_with_mounts` can error out via `?` before the REPL (or progress containing the REPL) is restored. On those early-return paths `put_shared_repl` is never reached, so the mutex stays `None` and the REPL is lost forever.</comment>
<file context>
@@ -602,30 +631,98 @@ impl MontyRepl {
+ macro_rules! feed_start_impl {
+ ($repl:expr) => {{
+ let mut print_cb = match &print_callback_ref {
+ Some(func) => Some(CallbackStringPrint::new_js_ref(env, func)?),
+ None => None,
+ };
</file context>
| if (this.mode !== 'read-only' && this.mode !== 'read-write') { | ||
| throw new TypeError("VirtualMount mode must be 'read-only' or 'read-write'") | ||
| } | ||
| if (options.writeBytesLimit != null && options.writeBytesLimit < 0) { |
There was a problem hiding this comment.
P2: writeBytesLimit validation accepts NaN and Infinity, which silently disable write quota enforcement
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/monty-js/wrapper.ts, line 145:
<comment>`writeBytesLimit` validation accepts NaN and Infinity, which silently disable write quota enforcement</comment>
<file context>
@@ -46,23 +47,179 @@ export type {
+ if (this.mode !== 'read-only' && this.mode !== 'read-write') {
+ throw new TypeError("VirtualMount mode must be 'read-only' or 'read-write'")
+ }
+ if (options.writeBytesLimit != null && options.writeBytesLimit < 0) {
+ throw new TypeError('writeBytesLimit must be non-negative')
+ }
</file context>
| if (options.writeBytesLimit != null && options.writeBytesLimit < 0) { | |
| if (options.writeBytesLimit != null && (!Number.isFinite(options.writeBytesLimit) || options.writeBytesLimit < 0)) { |
| mount.assertWritable(normalizedPath) | ||
| const data = getStringArg(call, 1, 'Path.write_text data') | ||
| const bytes = Buffer.byteLength(data) | ||
| mount.chargeWrite(bytes) |
There was a problem hiding this comment.
P2: Write limit quota is charged before backend write success, with no rollback on failure
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/monty-js/wrapper.ts, line 1128:
<comment>Write limit quota is charged before backend write success, with no rollback on failure</comment>
<file context>
@@ -612,6 +954,411 @@ export class MontyComplete {
+ mount.assertWritable(normalizedPath)
+ const data = getStringArg(call, 1, 'Path.write_text data')
+ const bytes = Buffer.byteLength(data)
+ mount.chargeWrite(bytes)
+ return normalizeWriteResult(callRequired(backend.writeText, 'writeText', normalizedPath, data), [...data].length)
+ }
</file context>
Summary
Adds first-class JavaScript virtual filesystem mounts for Monty, independent of the external-function support path.
MountDirhandling as the fast path and falls back to paused OS calls only when neededVirtualMountsupport in the JS wrapper with sync and async backends, read-only/read-write modes, write limits, and path routingfeedStart, syncfeed, and asyncfeedAsyncsupport for paused filesystem callsValidation
cargo fmtcargo check -p monty-jsnpm run build:debugnpm test -- __test__/virtual_mount.spec.tsnpm test -- __test__/mount.spec.ts __test__/start.spec.ts __test__/repl.spec.ts __test__/async.spec.ts __test__/virtual_mount.spec.tsnpm run lintcargo test -p monty-jsgit diff --checknpm test